
#region Imported Namespaces
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

using PHSRAG.Insight21.Admin;
using PHSRAG.Insight21.Context;
using PHSRAG.Insight21.SearchContext;
using AlphaPicker = PHSRAG.WebControls.AlphaPicker;
using Annunciator = PHSRAG.WebControls.Annunciator;
using ActivityIndicator = PHSRAG.WebControls.ActivityIndicator;
using DataAccess = PHSRAG.Utility.DataAccess;
using Settings = PHSRAG.Utility.Settings;
using Pager = PHSRAG.WebControls.Pager;
using Policy = PHSRAG.Insight21.Policy;
#endregion

namespace PHSRAG.Insight21.Admin.Controls
{
	/// <summary>
	/// The Picker class manages the activities of loading and displaying PI data.  The display
	/// takes the form of a DataGrid which allows the user to select a PI by clicking on "Selected" checkbox column in a row.
	/// Since there could be many rows, the DataGrid supports pagination through the Pager control.
	/// </summary>
	public partial class PIAccessPicker : System.Web.UI.UserControl
	{
		#region Constants
		private const string ClientScriptKey = "clientScript_0FDE002C-FF6E-4d83-82EB-2A14B9E83254";
		private const string DataKey = "data_0FDE002C-FF6E-4d83-82EB-2A14B9E83254";
		private const string FilterKey = "filter_0FDE002C-FF6E-4d83-82EB-2A14B9E83254";
		private const string FilteredDataKey = "filteredData_0FDE002C-FF6E-4d83-82EB-2A14B9E83254";
		private const string JavascriptViewAllCallback = "ResetPickerState";
        private const string JavascriptWindowTitle = "SetWindowTitle";
        private const string PIRightsCacheKey = "piRights_0FDE002C-FF6E-4d83-82EB-2A14B9E83254";
		#endregion

		#region Instance Variables (private)
		private Policy.ICachePolicy	cachePolicy;
		private Policy.ICachePolicy	pageCachePolicy;
		private string				pickerFilterColumn;
		#endregion

		#region Private Properties
		/// <summary>
		/// Get/Set the data used to initialize the AlphaPicker and the data grid. So as to avoid multiple hits on the
		/// database, the retrieved data is cached under a picker-specific key.
		/// </summary>
		private DataTable FilteredData
		{
			get
			{
				DataTable filteredDataTable = Session[PickerKeyForFilteredData] as DataTable;
				return (filteredDataTable == null) ? PickerData : filteredDataTable;
			}
			set
			{
				Session[PickerKeyForFilteredData] = value;
			}
		}

		/// <summary>
		/// Get/Set the Selected PI's the cached user has or will have access to.
		/// </summary>
		private DataView SelectedPIs
		{
			get
			{
				Hashtable h = (Hashtable) pageCachePolicy.GetCachedObject(Keys.IncomingDataKey); 
				DataView dataView = h[Keys.PIRightsKey] as DataView;

				if (dataView == null)
				{
					dataView = new DataView(User.GetPortfolioRights(0).PIList.Copy());
					dataView.RowStateFilter = DataViewRowState.Added | DataViewRowState.Unchanged;
					dataView.Sort = "Name ASC";
				}
				return dataView;
			}
			set
			{
				Hashtable h = (Hashtable) pageCachePolicy.GetCachedObject(Keys.IncomingDataKey); 
				h[Keys.PIRightsKey] = value;
				pageCachePolicy.Cache(Keys.IncomingDataKey, h); 
			}
		}
		
		/// <summary>
		/// Get/Set the data used to initialize the AlphaPicker and the data grid. So as to avoid multiple hits on the
		/// database, the retrieved data is cached under a picker-specific key.
		/// </summary>
		private DataTable PickerData
		{
			get
			{
				return ((UserContext)cachePolicy.GetCachedObject(UserContext.CacheKey)).PIList;
			}
		}

		private string PickerKeyForFilteredData
		{
			get { return string.Format("filtered_{0}", DataKey); }
		}

		/// <summary>
		/// Gets the relevant user object from the cache if there is one otherwise loads 
		/// a new one based on the cached personID and cache's it.
		/// </summary>
		private User User
		{
			get
			{
				User user = cachePolicy.GetCachedObject(User.CacheKey) as Admin.User;

				Hashtable h = (Hashtable) pageCachePolicy.GetCachedObject(Keys.IncomingDataKey); 
				int personId = (int)h[Keys.PersonIdKey];
				if (user == null || (user.PersonId != personId))
				{
					user = new User(cachePolicy);
					user.Load(Utility.DataAccess.GetConnectionString(), personId);
					user.Cache();
				}

				return user;
			}
		}

		#endregion

		#region Pagination Event Handlers
		/// <summary>
		/// Handler for the FirstPage event raised by the Pager. Force the data grid to display the first page.
		/// </summary>
		/// <param name="sender">Page control</param>
		/// <param name="e">Arguments associated with the event</param>
		/// <remarks>
		/// If the data grid is already displaying the first page, no further action is taken.
		/// </remarks>
		protected void OnFirstPage(object sender, System.Web.UI.WebControls.CommandEventArgs e)
		{
			piDataGrid.CurrentPageIndex = 0;
			LoadPIDataGrid();
		}

		/// <summary>
		/// Handler for the LastPage event raised by the Pager. Force the data grid to display the last page.
		/// </summary>
		/// <param name="sender">Page control</param>
		/// <param name="e">Arguments associated with the event</param>
		/// <remarks>
		/// If the data grid is already displaying the last page, no further action is taken.
		/// </remarks>
		protected void OnLastPage(object sender, System.Web.UI.WebControls.CommandEventArgs e)
		{
			piDataGrid.CurrentPageIndex = pager.PageCount - 1;
			LoadPIDataGrid();
		}

		/// <summary>
		/// Handler for the NextPage event raised by the Pager. Force the data grid to display the next page.
		/// </summary>
		/// <param name="sender">Page control</param>
		/// <param name="e">Arguments associated with the event</param>
		/// <remarks>
		/// If the data grid is already displaying the last page, no further action is taken.
		/// </remarks>
		protected void OnNextPage(object sender, System.Web.UI.WebControls.CommandEventArgs e)
		{
			piDataGrid.CurrentPageIndex++;
			LoadPIDataGrid();
		}

		/// <summary>
		/// Handler for the PreviousPage event raised by the Pager. Force the data grid to display the previous page.
		/// </summary>
		/// <param name="sender">Page control</param>
		/// <param name="e">Arguments associated with the event</param>
		/// <remarks>
		/// If the data grid is already displaying the first page, no further action is taken.
		/// </remarks>
		protected void OnPreviousPage(object sender, System.Web.UI.WebControls.CommandEventArgs e)
		{
			piDataGrid.CurrentPageIndex--;
			LoadPIDataGrid();
		}
		#endregion

		#region Event Handlers

		/// <summary>
		/// Clears the PI Selection Criteria and reloads the search results with the default search.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void OnClear(object sender, System.EventArgs e)
		{
			searchText.Text = string.Empty;
			GetPIList();
		}
		/// <summary>
		/// In the search results, if the PI is already selected, disable the checkbox in the "Selected" column.
		/// </summary>
		/// <param name="sender">Control being loaded</param>
		/// <param name="e">Argument associated with the event</param>
		protected void OnItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
		{
			if ((e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
				&& (SelectedPIs.Table.Select( String.Format("Id = {0}", e.Item.Cells[0].Text)).Length > 0))
					((CheckBox)(e.Item.Cells[4]).Controls[1]).Enabled = false;
		}

		/// <summary>
		/// On first load of the control, initialize the AlphaPicker and load the DataGrid with PI data.
		/// </summary>
		/// <param name="sender">Control being loaded</param>
		/// <param name="e">Argument associated with the event</param>
		protected void Page_Load(object sender, System.EventArgs e)
		{
			annunciator.Title = string.Empty;
			annunciator.Message = string.Empty;

			if (!IsPostBack)
			{
				//remove the cached data views that the pickers may have populated previously
				Hashtable h = (Hashtable) pageCachePolicy.GetCachedObject(Keys.IncomingDataKey); 
				h.Remove(Keys.PIRightsKey);

				alphaPicker.DataSource = PickerData;
				alphaPicker.DataColumn = pickerFilterColumn;
				alphaPicker.JavascriptViewAllCallback = JavascriptViewAllCallback;

				piDataGrid.PageSize = Settings.GetKeyValue("DataGrid.ItemsPerPage", 10);

				LoadPIDataGrid();


				LoadSelectedPIDataGrid();
			}
		}

		/// <summary>
		/// Copy the selectedPI's table to the cached user's portfolio rights table a
		/// and persist the cached user's portfolio rights.  The popup will closes
		/// and refresh it's parent window, reloading the user access rights page.
		/// </summary>
		/// <param name="sender">Control being loaded</param>
		/// <param name="e">Argument associated with the event</param>
		protected void OnSave(object sender, System.EventArgs e)
		{
			PortfolioAccessRights portfolioRights = User.GetPortfolioRights(0);

			portfolioRights.PIList = SelectedPIs.Table;

			SqlConnection connection = new SqlConnection(DataAccess.ConnectionString);			
			SqlTransaction transaction = null;
			try
			{
				connection.Open();
				transaction = connection.BeginTransaction("PortfolioAccessRights");
				portfolioRights.Save(transaction );
				User.UpdateUserResourceCache(transaction);
				transaction.Commit();
			}
			catch(Exception ex)
			{
				if(transaction != null)
					transaction.Rollback();

				annunciator.Message = ex.Message;
			}
			finally
			{
				if (connection.State != ConnectionState.Closed)
					connection.Close();
			}
			
			User.UnCache();
			Page.ClientScript.RegisterClientScriptBlock(GetType( ), ClientScriptKey, "<script language='javascript'>window.opener.document.location.href='../Admin/AdminUserAccessRights.aspx';window.close();</script>");
			cachePolicy.Cache(Keys.SaveEditStateKey, true); 
		}

		/// <summary>
		/// Handler for the Search button. Helps in filtering the data displayed in the data grid.
		/// </summary>
		/// <param name="sender">The control which raised the event</param>
		/// <param name="e">Argument associated with the event</param>
		/// <remarks>
		/// <p>There are two inputs to the search action:</p>
		/// <list type="bullet">
		/// <item><description>An alphabet from the AlphaPicker</description></item>
		/// <item><description>Free-form text typed in the search text box</description></item>
		/// </list>
		/// <p>If any text is explicitly typed in the text box, it will override any alphabet selection.</p>
		/// <p>A search always implies displaying the first page of the data grid.</p>
		/// </remarks>
		protected void OnSearch(object sender, System.EventArgs e)
		{
			GetPIList();
		}

		/// <summary>
		/// Any item selected on the search results data grid will be added to the selected
		/// datagrid's data source and both grids are reloaded.
		/// </summary>
		/// <param name="sender">The control which raised the event</param>
		/// <param name="e">Argument associated with the event</param>
		protected void OnSelectPIs(object sender, System.EventArgs e)
		{
			DataView dv = SelectedPIs;
			foreach (DataGridItem item in piDataGrid.Items)
			{
				if (item.ItemType == ListItemType.Item || item.ItemType == ListItemType.AlternatingItem || item.ItemType == ListItemType.SelectedItem)
				{
					if ( ((CheckBox)(item.Cells[4]).Controls[1]).Checked == true)
					{
						DataRow row = dv.Table.NewRow();
						row["Id"] = Convert.ToInt32(item.Cells[0].Text);
						row["Name"] = item.Cells[1].Text;
						row["Username"] = item.Cells[2].Text;
						row["Organization"] = item.Cells[3].Text;
						row["FwkDomainUserId"] = User.DomainUserId;
						row["RightBitMask"] = 0;

						dv.Table.Rows.Add(row);
					}
				}
			}
            
			SelectedPIs = dv;
			LoadPIDataGrid();
			LoadSelectedPIDataGrid();
		}

		/// <summary>
		/// Any item selected on the selected datagrid data grid will be removed from the selected
		/// datagrid's data source and both grids are reloaded.
		/// </summary>
		/// <param name="sender">The control which raised the event</param>
		/// <param name="e">Argument associated with the event</param>
		protected void OnRemovePIs(object sender, System.EventArgs e)
		{
			DataView dv = SelectedPIs;
			foreach (DataGridItem item in selectedPIDataGrid.Items)
			{
				if ((item.ItemType == ListItemType.Item || item.ItemType == ListItemType.AlternatingItem || item.ItemType == ListItemType.SelectedItem)
					&& ( ((CheckBox)(item.Cells[4]).Controls[1]).Checked == true))
						dv.Table.Select(String.Format("Id = {0}", item.Cells[0].Text))[0].Delete();
			}
			SelectedPIs = dv;
			LoadPIDataGrid();
			LoadSelectedPIDataGrid();
		}
		#endregion

        #region Overrides
        /// <summary>
        /// Register clientside scripts.
        /// </summary>
        /// <param name="e">Argument associated with the event.</param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            if (!Page.ClientScript.IsClientScriptBlockRegistered(JavascriptWindowTitle))
            {
                string script =
                    "<script language=\"javascript\">\n" +
                    "\twindow.pageTitle.text = \"PI Access Search\";\n" +
                    "</script>\n";

                Page.ClientScript.RegisterClientScriptBlock(GetType( ), JavascriptWindowTitle, script);
            }
        }
        #endregion

		#region Private Methods

		/// <summary>
		/// Applies PI filter criteria and loads the results into the PI data grid.
		/// </summary>
		private void GetPIList()
		{
			string filter = searchText.Text.Trim();
			if (filter.Length == 0)
				filter = alphaPicker.Selection;
			else
				alphaPicker.Selection = filter.Substring(0, 1);

			DataTable pickerData = PickerData;
			DataRow[] rows = pickerData.Select(string.Format("{0} LIKE '{1}%'", pickerFilterColumn, filter));
			if (rows.Length > 0)
			{
				DataTable filteredData = pickerData.Clone();
				foreach (DataRow row in rows)
					filteredData.ImportRow(row);
				FilteredData = filteredData;

				piDataGrid.CurrentPageIndex = 0;
				LoadPIDataGrid();
				select.Visible = piDataGrid.Visible = pager.Visible = true;
			}
			else
			{
				annunciator.Title = "Data Load Warning:";
				annunciator.Message = string.Format("No PI data found matching the search criteria.");
				annunciator.Visible = true;
				select.Visible = piDataGrid.Visible = pager.Visible = false;
			}
		}
		/// <summary>
		/// Load the DataGrid with PI data. 
		/// Initialize the Pager based on the volume of data retrieved from the database. The navigation links on the
		/// Pager are also set.
		/// </summary>
		/// <remarks>
		/// The data that is loaded into the data grid is filtered based upon the selection in the alpha picker.
		/// </remarks>
		private void LoadPIDataGrid()
		{
			DataTable pickerData = FilteredData;
			pager.RecordCount = pickerData.Rows.Count;

			int pageCount = (pager.RecordCount / piDataGrid.PageSize) + (int)(((pager.RecordCount % piDataGrid.PageSize) == 0) ? 0 : 1);
			pager.PageCount = pageCount;
			pager.CurrentPage = piDataGrid.CurrentPageIndex + 1;	// Pager uses 1-based index.

			piDataGrid.DataSource = pickerData;
			piDataGrid.DataBind();

			alphaPicker.Visible = searchText.Visible = search.Visible = pager.Visible = select.Visible = (piDataGrid.Items.Count > 0);

			if (pager.Visible)
			{
				int startIndex = piDataGrid.CurrentPageIndex * piDataGrid.PageSize;
				pager.FirstRecord = startIndex + 1;
				pager.LastRecord = startIndex + Math.Min(startIndex + piDataGrid.PageSize, piDataGrid.Items.Count);

				ManageNavigationLinks();
			}
			else
			{
				UserContext userContext = cachePolicy.GetCachedObject(UserContext.CacheKey) as UserContext;
				System.Diagnostics.Trace.Assert(userContext != null);

				annunciator.Title = "Data Load Warning:";
				annunciator.Message = string.Format("No PI data found for {0} {1}.", 
					userContext.FirstName, userContext.LastName);
				annunciator.Visible = true;
				piDataGrid.Visible = false;

				Controls.Add(new LiteralControl(
					"<input type=\"button\" class=\"Button\" value=\"Close Window\" onclick=\"window.close();return false;\">"));
			}
		}

		/// <summary>
		/// Load the DataGrid with the cached user's access rights and any selections that have been made.
		/// </summary>
		private void LoadSelectedPIDataGrid()
		{
			selectedPIDataGrid.DataSource = SelectedPIs;
			selectedPIDataGrid.DataBind();

			remove.Visible = (selectedPIDataGrid.Items.Count > 0);
		}

		/// <summary>
		/// Set the First/Last/Next/Previous page links on the Pager control to enabled/disabled based on which page is
		/// being displayed in the DataGrid.
		/// The First/Previous links are disabled if the first page is being displayed.
		/// The Last/Next links are disabled if the last page is being displayed.
		/// </summary>
		private void ManageNavigationLinks()
		{
			pager.EnableFirstPage = pager.EnablePreviousPage = (piDataGrid.CurrentPageIndex != 0);
			pager.EnableLastPage = pager.EnableNextPage = (piDataGrid.CurrentPageIndex != (pager.PageCount - 1));
		}
		#endregion

		#region Web Form Designer generated code
		/// <summary>
		/// Initialize the control. Create dynamic columns in the data grid based on the pick type.
		/// </summary>
		/// <param name="e">Argument associated with the event</param>
		override protected void OnInit(EventArgs e)
		{
			//
			// CODEGEN: This call is required by the ASP.NET Web Form Designer.
			//
			InitializeComponent();
			base.OnInit(e);

			cachePolicy = (Policy.ICachePolicy)Page.Session[Policy.Names.CachePolicyKey];
			pageCachePolicy = (Policy.ICachePolicy)Page.Session[Policy.Names.CachePolicyKey];
			pickerFilterColumn = Request.QueryString["FilterColumn"];

			searchText.Attributes["onchange"] = string.Format("{0}();", AlphaPicker.JavascriptResetFunction);
		}
		
		/// <summary>
		///		Required method for Designer support - do not modify
		///		the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
		}
		#endregion
	}
}

// --- EOF ---
